/**
 * 文件名: once_flag_example_test.cpp
 *
 * 构建与运行（示例命令，需已安装 GoogleTest）：
 *   g++ -std=c++20 -pthread once_flag_example_test.cpp -lgtest -lgtest_main -o once_flag_example_test
 *   ./once_flag_example_test
 *
 * 示例覆盖内容：
 *   1. 多线程同时调用同一个初始化函数，验证函数只执行一次。
 *   2. 初始化函数首次抛出异常，确保 std::call_once 会重新尝试执行并最终成功。
 */

#include <gtest/gtest.h>

#include <mutex>   // std::once_flag, std::call_once
#include <thread>  // std::thread

// -----------------------------------------------------------------------------
// 场景 1：多个线程并发调用相同初始化逻辑，验证 initializer 只执行一次。
// -----------------------------------------------------------------------------
TEST(OnceFlagTest, InitializerRunsOnlyOnceAcrossThreads) {
    std::once_flag flag; // 控制初始化只执行一次
    int initCount = 0;   // 统计初始化函数被执行的次数

    // 初始化函数：简单地对计数器加一
    auto initializer = [&] {
        // 当多个线程同时到达这里时，std::call_once 会保证只有一个线程进入
        ++initCount;
    };

    // worker 函数：线程体，仅调用 std::call_once
    auto worker = [&]() {
        // std::call_once 内部会检测 flag，如果是第一次调用则执行 initializer
        // 否则立即返回。所有线程共享同一个 flag，因此 initializer 最终只调用一次。
        std::call_once(flag, initializer);
    };

    // 启动多个线程并发调用 worker
    std::thread t1(worker);
    std::thread t2(worker);
    std::thread t3(worker);
    std::thread t4(worker);

    // 等待所有线程结束
    t1.join();
    t2.join();
    t3.join();
    t4.join();

    // 检查：初始化逻辑应只执行一次
    EXPECT_EQ(initCount, 1);
}

// -----------------------------------------------------------------------------
// 场景 2：初始化函数第一次抛出异常。
//        std::call_once 不会设置 flag，下一次调用会再次尝试执行。
// -----------------------------------------------------------------------------
TEST(OnceFlagTest, InitializerRetriesAfterException) {
    std::once_flag flag;     // 用于一次性初始化
    int attemptCount = 0;    // 记录初始化尝试次数
    bool initializationDone = false; // 标记最终是否完成初始化

    // 初始化函数：第一次调用故意抛出异常，第二次才成功
    auto initializer = [&]() {
        ++attemptCount;

        if (attemptCount == 1) {
            // 第一次执行时抛出异常，模拟初始化失败
            throw std::runtime_error("initialization failed");
        }

        // 第二次执行：设置初始化完成标志
        initializationDone = true;
    };

    // 第一次调用：预期捕获异常
    EXPECT_THROW(std::call_once(flag, initializer), std::runtime_error);

    // 第二次调用：由于第一次抛出异常，flag 未被标记，call_once 会重新尝试
    EXPECT_NO_THROW(std::call_once(flag, initializer));

    // 第三次调用：此时 flag 已经标记成功，initializer 不会再执行
    EXPECT_NO_THROW(std::call_once(flag, initializer));

    // 验证：初始化尝试过至少两次（第一次失败，第二次成功）
    EXPECT_EQ(attemptCount, 2);
    EXPECT_TRUE(initializationDone);
}